package com.eui.arch.util
{
	import com.adobe.utils.ArrayUtil;

	import flash.utils.describeType;

	import mx.collections.ArrayCollection;
	import mx.utils.*;


	public class ObjectUtil
	{


		/*
		 * areDifferentDates()
		 *
		 * Like areDifferent, but only use the date portion of the Date object, not the time
		 * portion, for comparative significance:
		 */

		// TODO: areDifferentDates and areDifferentTimes could be aggregated into one function.

		public static function areDifferentDates(value1:Date, value2:Date, nees:Boolean):Boolean
		{
			var m1:int;
			var m2:int;
			var d1:int;
			var d2:int;
			var y1:int;
			var y2:int;

			if (value1 == null || value2 == null)
				return dateCompare(value1, value2, true) != 0;

			m1=value1.getMonth();
			m2=value2.getMonth();
			y1=value1.getFullYear();
			y2=value2.getFullYear();
			d1=value1.getDate();
			d2=value2.getDate();

			return (m1 != m2 || y1 != y2 || d1 != d2);
		}


		public static function areDifferentTimes(value1:Date, value2:Date, nees:Boolean):Boolean
		{
			var h1:int;
			var h2:int;
			var m1:int;
			var m2:int;
			var s1:int;
			var s2:int;
			var valuea:Date;
			var valueb:Date;

			if (value1 == null || value2 == null)
				return dateCompare(value1, value2, true) != 0;

			h1=value1.getHours();
			h2=value2.getHours();
			m1=value1.getMinutes();
			m2=value2.getMinutes();
			s1=value1.getSeconds();
			s2=value2.getSeconds();

			valuea=new Date(2000, 1, 1, h1, m1, s1);
			valueb=new Date(2000, 1, 1, h2, m2, s2);

			return dateCompare(valuea, valueb, true) != 0;
		}

		/**
		 * Returns true if value1 is different from value2 (does a deep comparison).
		 *
		 * @param nullEqualsEmptyString specifies whether null should be considered equal to empty string
		 */
		public static function areDifferent(value1:Object, value2:Object, nullEqualsEmptyString:Boolean=false):Boolean
		{

			if (value1 is Date && value2 is Date)
			{
				return dateCompare(value1 as Date, value2 as Date, true) != 0;
			}

			if (!nullEqualsEmptyString || !(value1 is String || value2 is String))
			{
				return (mx.utils.ObjectUtil.compare(value1, value2) != 0 || (value1 == null && value2 != null) || (value1 != null && value2 == null))
			}
			else
			{
				//should only get here if nullEqualsEmptyString is true and one of the values is a String
				return !(value1 == value2 || (value1 == null && value2 == '') || (value1 == '' && value2 == null));
			}
		}

		/**
		 * Compares dates without times.
		 *
		 *  @param a Date object.
		 *
		 *  @param b Date object.
		 *
		 *  @return 0 if <code>a</code> and <code>b</code>
		 *  are <code>null</code> or equal;
		 *  -1 if <code>a</code> is <code>null</code>
		 *  or before <code>b</code>;
		 *  1 if <code>b</code> is <code>null</code>
		 *  or before <code>a</code>.
		 */
		public static function dateCompare(a:Date, b:Date, compareTime:Boolean=false):int
		{
			if (a == null && b == null)
			{
				return 0;
			}

			if (a == null)
				return -1;

			if (b == null)
			{
				return 1;
			}

			var na:Number;
			var nb:Number;

			if (!compareTime)
			{
				var date1:Date=new Date(a.fullYear, a.month, a.date);
				var date2:Date=new Date(b.fullYear, b.month, b.date);
				na=date1.getTime();
				nb=date2.getTime();
			}
			else
			{
				na=a.getTime();
				nb=b.getTime();
			}

			if (na < nb)
			{
				return -1;
			}

			if (na > nb)
			{
				return 1;
			}

			return 0;
		}

		/** Add / Substract given number of days and return new date */
		public static function dateAdd(date:Date, value:int=0):Date
		{
			var returnDate:Date;

			if (date)
			{
				returnDate=new Date(date.time);
				returnDate['date']+=value;
			}

			return returnDate;
		}

		public static function getObjectDescription(obj:*):XML
		{ // NO PMD
			// wrapping the describe type in try / catch so primitive type beans will not fail
			var objDesc:XML;

			try
			{
				var cacheDescription:DescribeTypeCacheRecord=DescribeTypeCache.describeType(obj);
				objDesc=cacheDescription.typeDescription;
			}
			catch (e:ReferenceError)
			{
				objDesc=describeType(obj);
			}
			return objDesc;
		}

		/**
		 *
		 * Make a copy of the source object's properties into target object.
		 *
		 */
		public static function fill(targetObj:*, sourceObj:*):void
		{ // NO PMD
			var classInfo:Object=mx.utils.ObjectUtil.getClassInfo(targetObj);

			for each (var prop:QName in classInfo.properties)
			{
				try
				{
					targetObj[prop.localName]=sourceObj[prop.localName];
				}
				catch (e:Error)
				{
					trace("fill error: " + prop.localName + " -- " + e);
				}
			}
		}

		/**
		 * This is a way to do a comparison on objects that may have uid in it, such as an object
		 * that has array and/or array collection inside of it.
		 */
		public static function areDifferentIgnoreUID(value1:Object, value2:Object):Boolean
		{

			var excludes:Array=['uid'];

			var props:Object=mx.utils.ObjectUtil.getClassInfo(value1, excludes);
			var different:Boolean=false;

			for each (var p:QName in props.properties)
			{

				var propName:String=p.localName;
				var propValue:Object=value1[propName];
				var propValue2:Object=value2[propName];

				different=areDifferent(mx.utils.ObjectUtil.toString(propValue), mx.utils.ObjectUtil.toString(propValue2));

				if (different)
				{

					if (propValue is Array || propValue is ArrayCollection)
					{

						var arr1:Array;
						var arr2:Array;

						if (propValue is Array)
						{
							arr1=propValue as Array;
							arr2=propValue2 as Array;
						}
						else
						{
							arr1=(propValue as ArrayCollection).source;
							arr2=(propValue2 as ArrayCollection).source;
						}

						different=areDifferentArrayCheck(arr1, arr2)
					}

					if (different)
					{
						//trace(" areDifferentIgnoreUID Difference at: " + propName + ":" + propValue);
						return different;
					}
				}
			}
			return different;
		}

		private static function areDifferentArrayCheck(array1:Array, array2:Array):Boolean
		{
			if (array1.length != array2.length)
			{
				return true;
			}
			else
			{

				for (var i:int=0; i < array1.length; i++)
				{
					if (compareObjects(array1[i], array2[i]) == true)
					{
						return true;
					}
				}

				return false;
			}
		}

		private static function compareObjects(value1:Object, value2:Object):Boolean
		{
			var excludes:Array=['uid'];

			var props:Object=mx.utils.ObjectUtil.getClassInfo(value1, excludes);
			var different:Boolean=false;

			for each (var p:QName in props.properties)
			{

				var propName:String=p.localName;
				var propValue:Object=value1[propName];
				var propValue2:Object=value2[propName];

				different=areDifferent(mx.utils.ObjectUtil.toString(propValue), mx.utils.ObjectUtil.toString(propValue2));

				if (different == true)
				{
					//trace("compareObjects Difference at: " + propName + ":" + propValue);
					if (propValue is Array || propValue is ArrayCollection)
					{
						var arr1:Array;
						var arr2:Array;

						if (propValue is Array)
						{
							arr1=propValue as Array;
							arr2=propValue2 as Array;
						}
						else
						{
							arr1=(propValue as ArrayCollection).source;
							arr2=(propValue2 as ArrayCollection).source;
						}
						different=areDifferentArrayCheck(arr1, arr2)
					}
					else
					{
						different=compareObjects(propValue, propValue2)
						return different;
					}

				}
			}
			return different;
		}
	}
}